本篇文章以brave clojure的第3章為靈感來介紹clojure基礎語法及特色。
Ref: Chapter 3: Do Things: A Clojure Crash Course
在clojure(或LISP,LISt Processing,列表處理器)的語法,
有3個主要特色:
(verb param1 param2)
運算子operator
會先擺在第一順位,運算元 operand
放後面。
(operator operand1 operand2 ... operandn)
;;
(+ 1 1)
;=> 2
(operator operand1 operand2 ... operandn)
第一個位置 + 運算子operator
,
或者也可以換成任何函式
名稱!
然後接著是一或多個參數,(代換掉上面的1 2。)
(str "中秋節" "一家烤肉" "萬家香")
; => "中秋節一家烤肉萬家香"
這樣的好處是,LISP程式可以把原始碼當作資料結構
進行操作。
你也能用函式定義新的運算子,寫出更複雜的程式。
表達資料流程flow control的 if else
是每個語言的入門語法,
在clojure裡,
排在第一個的if
就是剛剛everything is a list (verb param1 param2)
所說的 verb
(if boolean-form then-form optional-else-form)
但基本我們會縮排一下比較好看喇~
(if boolean-form
then-form
optional-else-form)
來用falsy的nil放在if condition裡舉個例子
(if nil
"This won't be true because nil is falsey"
"nil is falsey")
; => "nil is falsey"
以上的例子還是以簡單的字串str
舉例,
需要熟悉cheatsheet上的函式
才能做出更複雜的功能唷~
例如:以下的程式代表求取50以內的偶數平方和
(->> (range 50)
(filter even?)
(map (fn [x] (* x x)))
(reduce +))
其中的(fn [x] (* x x)) 就是個匿名函式
,可直接傳遞給map函式。
或者,如果想要使用有名字
的函式的話,你需要知道def & defn,來定義屬於你自己的函式。
(ns tutorial.core
(:gen-class))
(defn -main
[& args]
(println "hello world, Tingting!"))
我使用的IDE是 IntelliJ IDEA CE community版本,
再安裝Cursive plugin: the full Clojure and ClojureScript language support
按run執行結果會出現 "hello world, Tingting!"
def & defn差在哪呢?
這是新手一開始寫clojure第一個比較容易混淆的誤區
def這個special form可以視為是function,其用途是用來命名可以操作的變數
def Creates and interns or locates a global var with the name of symbol and a
namespace of the value of the current namespace (ns).
See http://clojure.org/special_forms for more information.
舉個例子?,我定義一個vector harry-potter-series
用來放我買的三本書
;; 如同前面提的,def放在第一個位置,所以它本人是function (operator / verb)
(def harry-potter-series
["the Philosopher's Stone" "the Chamber of Secrets" "the Prisoner of Azkaban"])
在command line執行 lein repl
~/Documents/Clojure/tutorial master*
❯ lein repl
nREPL server started on port 56722 on host 127.0.0.1 - nrepl://127.0.0.1:56722
REPL-y 0.4.4, nREPL 0.8.3
Clojure 1.10.1
OpenJDK 64-Bit Server VM 1.8.0_312-b07
Docs: (doc function-name-here)
(find-doc "part-of-name-here")
Source: (source function-name-here)
Javadoc: (javadoc java-object-or-class-here)
Exit: Control+D or (exit) or (quit)
Results: Stored in vars *1, *2, *3, an exception in *e
啟動REPL成功後,
輸入harry-potter-series
,三本書就列出來啦~
tutorial.core=> harry-potter-series
["the Philosopher's Stone" "the Chamber of Secrets" "the Prisoner of Azkaban"]
稍微改寫一下剛剛的hello world,讓它能傳參數進去
(defn -main
[number]
(println "hello world, Tingting!")
(println "I bought" number "harry potter series"))
呼叫main函式 (-main 3)
tutorial.core=> (-main 3)
hello world, Tingting!
I bought 3 harry potter series
nil
;;everything enclosure returns a value。println沒有回傳值,所以回傳nil
defn 文件裡有說明跟def是可以轉換的
Same as
(def name (fn [params* ] exprs*))
(def name (fn ([params* ] exprs*)+))
舉個例子,(defn x [a b] (+ a b))
等於def裡面放匿名函式 (def y (fn [a b] (+ a b)))
tutorial.core=> (defn x [a b] (+ a b))
#'tutorial.core/x
tutorial.core=> (def y (fn [a b] (+ a b)))
#'tutorial.core/y
tutorial.core=> (x 2 3)
5
tutorial.core=> (y 2 3)
5
很神奇吧!
多看幾次defn熟練之後就可以無痛轉換def了~
明天要來介紹javascript與clojurescript的語法差別:)